function Watcher(vm, exp, cb) {
    //this.==>watcher实例
    this.cb = cb;//在实例上暂存callback回调函数(修改页面用的更新函数)
    this.vm = vm;//MVVM的实例
    this.exp = exp;//对应的表达式
    this.depIds = {};//很多个相关dep
    this.value = this.get();//获取值 在初始化的时候获取当前对应表达式的值
}

//watcher的样子
/*let w = {
    cb: function(){},
    vm: vm,
    exp: "a",
    depIds: {//js存数据 要么就是[] 要么就是{}
        0: { id:0, subs:[watcher...] }
        1: { id:1, subs:[watcher...] }
        2: { id:2, subs:[watcher...] }
    },
    value: ""
}*/

Watcher.prototype = {
    //调用更新函数
    update: function() {
        //获取当前data中的表达式相关value值 最新的值
        var value = this.get();//只要获取值,那么就还会触发vm.data中相关数据的get方法
        var oldVal = this.value;//在watcher的value中保持着初始化的时候的相关表达式的值
        if (value !== oldVal) {//新旧值进行比较 来决定当前页面是否一定要更新
            this.value = value;//如果不同就先把最新的值覆盖掉之前的旧值 此时一直是在操作watcher中的value
            //有新的value有旧的value 还有vm 直接调用更新页面的回调函数
            //为了保证在更新页面的时候this的一致性,所以在把当前的vm给传递回去
            this.cb.call(this.vm, value, oldVal);
        }
    },
    //在dep中添加相关的watcher
    addDep: function(dep) {
        //如果当前的watcher的depIds中没有当前要添加的dep.id 才会进入判断
        //为了防止watcher重复与相关dep建立关系
        //重复建立关系(当数据被更新的时候,dep会去通知所有相关的watcher,watcher会调用run方法,run方法里第一句就是获取值,只要获取值,那么就还会触发vm.data中相关数据的get方法,此时就又可能会调用到当前的addDep方法,然后就通过hasOwnProperty判断之前是否已经建立关系)
        if (!this.depIds.hasOwnProperty(dep.id)) {
            //调用相关dep上面的addSub方法 添加watcher到dep的subs中 this==>watcher
            dep.addSub(this);
            //在watcher的depIds={}中增加当前的dep
            //this.depIds.0 = dep
            //在之前和dep已经建立关系,那么就把当前的dep存储到depIds中,以方便后续判断是否已经建立关系
            this.depIds[dep.id] = dep;
        }
    },
    //获取值的方法
    get: function() {
        //暂存当前相关的watcher到Dep.target上
        Dep.target = this;
        //真正去读取vm.data中的value值
        var value = this.getVMVal();//(通过获取value值,来触发数据劫持中的get方法,在get方法中去获取当前属性相关的dep 然后把当前watcher与dep建立关系)
        //watcher与dep建立关系,已经获取到value值了 置空当前的Dep.target防止意外再次触发
        Dep.target = null;
        return value;
    },
    //获取vm中data里相关的表达式的value
    getVMVal: function() {
        //把当前表达式通过. 拆分成数组
        var exp = this.exp.split('.');
        //把vm中的data赋值给val
        var val = this.vm._data;
        exp.forEach(function(k) {
            //对_data中的数据进行读操作(val[k]), 读取之后去赋值给val
            val = val[k];
        });
        return val;
    }
};